﻿using System;
using System.ComponentModel;
using System.IO;
using AutoMapper;
using Newtonsoft.Json;

namespace GurLoader.Preferences
{
    public class ApplicationPreferencesBase<T> : IEditableObject where T : class, new()
    {
        [JsonIgnore]
        public string FileName { get; private set; }

        private string BackupFilename { get; set; }
        private IFileOperations FileOperations { get; set; }

        public event EventHandler<EventArgs> PreferencesSaved;

        public ApplicationPreferencesBase() : this(GetDefaultFileName())
        {
        }

        public ApplicationPreferencesBase(string fileName) : this( fileName, new LocalIOFileOperations())
        {
        }

        public ApplicationPreferencesBase(string fileName, IFileOperations fileOperations)
        {
            if (string.IsNullOrEmpty(fileName))
                throw new ArgumentException("Filename must not be null.");

            if ( fileOperations == null )
                throw new ArgumentException("File operations must not be null.");

            FileName = fileName;
            FileOperations = fileOperations;

            Mapper.CreateMap<T, T>();
        }

        private static string GetDefaultFileName()
        {
            return Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                "GurLoader",
                "settings.json");
        }

        public virtual void Save()
        {
            Save(FileName);
        }

        public virtual void Save(string fileName, bool suppressSavedEvent = false)
        {
            //string directory = Path.GetDirectoryName(fileName);
            string directory = FileOperations.GetDirectoryName(fileName);
            if (! FileOperations.DirectoryExists(directory))
                FileOperations.CreateDirectory(directory);

            FileOperations.WriteAllText(fileName, JsonConvert.SerializeObject(this));
            if (!suppressSavedEvent)
                FirePreferencesSaved();
        }

        public static void Save(T pSettings, IFileOperations fileOperations, Action onComplete = null)
        {
            Save(pSettings, GetDefaultFileName(), fileOperations, onComplete);
        }

        public static void Save(T pSettings, string fileName, IFileOperations fileOperations, Action onComplete = null)
        {
            string directory = fileOperations.GetDirectoryName(fileName);
            if (!fileOperations.DirectoryExists(directory))
                fileOperations.CreateDirectory(directory);

            fileOperations.WriteAllText(fileName, JsonConvert.SerializeObject(pSettings));

            if (onComplete != null)
                onComplete();
        }

        public static T Load(IFileOperations fileOperations)
        {
            return Load(GetDefaultFileName(), fileOperations);
        }

        public static T Load(string fileName, IFileOperations fileOperations)
        {
            var t = new T();
            if (fileOperations.FileExists(fileName))
                t = JsonConvert.DeserializeObject<T>(fileOperations.ReadAllText(fileName));
            return t;
        }

        private readonly object lockObj = new object();

        public virtual void BeginEdit()
        {
            lock (lockObj)
            {
                if (!string.IsNullOrEmpty(BackupFilename))
                    return;

                BackupFilename = FileOperations.GetTempFileName();
                Save(BackupFilename, true);
            }
        }

        public virtual void EndEdit()
        {
            lock (lockObj)
            {
                if ( FileOperations.FileExists(BackupFilename))
                    FileOperations.DeleteFile(BackupFilename);
                BackupFilename = string.Empty;
            }
        }

        public virtual void CancelEdit()
        {
            lock (lockObj)
            {
                T t = Load(BackupFilename, FileOperations);
                Mapper.Map<T, T>(t, this as T);
                
                if ( FileOperations.FileExists(BackupFilename))
                    FileOperations.DeleteFile(BackupFilename);

                BackupFilename = string.Empty;
            }
        }

        protected void FirePreferencesSaved()
        {
            if (PreferencesSaved != null)
                PreferencesSaved(this, EventArgs.Empty);
        }
    }
}